package com.healthcarex.hip.saas.common.util.xml;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;


public class XmlHelper {

    private static final Logger logger = LoggerFactory
            .getLogger(XmlHelper.class);

    public static final String ENCODING_UTF8 = "UTF-8";

    public static final String FLAG_YES = "yes";

    public static final TransformerFactory transFactory = TransformerFactory.newInstance();

    static Pattern charP = Pattern.compile("&#(x[0-9a-zA-Z]*|[0-9]*);");

    /**
     * 判断字符串是否xml文档
     *
     * @param str
     * @return
     */
    public static boolean isXmlDocument(String str) {
        boolean flag = true;
        try {
            StringReader sr = new StringReader(str);
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

            factory.setNamespaceAware(true);
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(new InputSource(sr));
        } catch (Exception e) {
            flag = false;
        }
        return flag;
    }

    /**
     * 加载XML String资源
     *
     * @param xmlString xml格式的字符串
     * @return Node
     */
    public static Document loadXMLResource(String xmlString, String encoding) {
        if (0xFEFF == xmlString.charAt(0)) {
            xmlString = xmlString.substring(1);
        }
        InputSource source = new InputSource(new BufferedReader(
                new StringReader(xmlString)));

        return xmlSourceToDocument(source, encoding);
    }

    /**
     * 加载XML byte[]资源
     *
     * @param xmlByte
     * @param encoding
     * @return
     */
    public static Document loadXMLResource(byte xmlByte[], String encoding) {
        String xmlString = "";
        try {
            xmlString = new String(xmlByte, encoding);
            return loadXMLResource(xmlString, encoding);
        } catch (UnsupportedEncodingException e) {
            logger.error(e.getMessage());
        }
        return null;
    }

    /**
     * 加载XML File资源
     *
     * @param xmlFile xml文件
     * @return Node
     */
    public static Document loadXMLResource(File xmlFile, String encoding) {
        InputSource source = null;
        try {
            source = new InputSource(new FileInputStream(xmlFile));
        } catch (FileNotFoundException e) {
            logger.error(e.getMessage());
        }

        return xmlSourceToDocument(source, encoding);
    }

    /**
     * 把xml source 转换为Document
     *
     * @param source
     * @return
     */
    private static Document xmlSourceToDocument(InputSource source,
                                                String encoding) {
        source.setEncoding(encoding);
        Document document = null;
        try {
            document = loadDocument(source);
        } catch (SAXParseException spe) {
            if (null != spe.getSystemId()) {
                logger.error("xpath解析错误，出错的行数是：{}，uri：{}", spe.getLineNumber(),
                        spe.getSystemId());
            }
            logger.debug(spe.getMessage());
        } catch (SAXException se) {
            document = null;
            logger.debug("解析XML错误，请确保存在格式正确的XML文档。");
        } catch (IOException ioe) {
            document = null;
            logger.debug("不能加载文档，文档不可读取。");
        }
        return document;
    }

    /**
     * 从InputSource加载document
     *
     * @param source
     * @return Node
     * @throws SAXException
     * @throws IOException
     */
    public static Document loadDocument(InputSource source)
            throws SAXException, IOException {
        Document document = null;
        DocumentBuilder parser = null;
        DocumentBuilderFactory domFactory = DocumentBuilderFactory
                .newInstance();
        domFactory.setNamespaceAware(true);
        domFactory.setValidating(false);
        try {
            parser = domFactory.newDocumentBuilder();
        } catch (ParserConfigurationException pce) {
            logger.error(pce.getMessage());
        }
        document = parser.parse(source);
        return document;
    }


    /**
     * c
     *
     * @param doc
     * @return
     */
    public static String W3CDomAsXml(Document doc) {
        StringWriter sw = new StringWriter();
        Transformer transformer;
        try {
            transformer = transFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.ENCODING, ENCODING_UTF8);
            transformer.setOutputProperty(OutputKeys.INDENT, FLAG_YES);
            DOMSource xmlSource = new DOMSource(doc);
            Result output = new StreamResult(sw);
            transformer.transform(xmlSource, output);
        } catch (Exception e) {
            logger.error("domcument transform failure!", e);
        }

        return sw.getBuffer().toString();
    }

    public static String removeInvalidCharRefs(String message) {

        Matcher m = charP.matcher(message);
        StringBuffer sbr = new StringBuffer();
        String item = null;
        while (m.find()) {
            m.appendReplacement(sbr, "");
            item = message.substring(m.start(), m.end());
            if (isValidCharRef(item)) {
                sbr.append(item);
            } else {
                logger.info("删除非法字符引用{}", item);
            }
        }
        m.appendTail(sbr);

        return sbr.toString();
    }

    public static boolean isValidCharRef(String item) {
        if ((item == null) || ("&#;".equalsIgnoreCase(item)) || ("&#x;".equalsIgnoreCase(item))) {
            return false;
        }
        int radix = 10;
        if (item.startsWith("&#x")) {
            radix = 16;
        }
        String h = item.substring(2, item.length() - 1);
        if (radix == 16) {
            h = h.substring(1);
        }
        int ch = Integer.valueOf(h, radix).intValue();
        if ((ch == 9) || (ch == 10) || (ch == 13) ||
                ((ch >= 32) && (ch <= 55295)) ||
                ((ch >= 57344) && (ch <= 65533)) || (
                (ch >= 65536) && (ch <= 1114111))) {
            return true;
        }
        return false;
    }
}
